//
//  This file is part of a reduced file concatenation of the CwlViews
//  framework with internal interfaces for source inclusion in projects instead
//  of library inclusion.
//  For details, visit: https://github.com/mattgallagher/CwlViews
//
//  Copyright © 2015-2018 Matt Gallagher ( http://cocoawithlove.com ). All rights reserved.
//
//  Permission to use, copy, modify, and/or distribute this software for any
//  purpose with or without fee is hereby granted, provided that the above
//  copyright notice and this permission notice appear in all copies.
//
//  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
//  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
//  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
//  SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
//  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
//  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
//  IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
//  This file was generated by the CwlUtilsConcat tool on 2018-04-02 04:01:17 +0000 from the following files:
//
//    CwlLayout.swift
//

#if os(macOS)
	import AppKit
#else
	import UIKit
#endif

// MARK: ### CwlLayout.swift ###

#if os(macOS)
	import AppKit
	
	protocol ViewConvertible {
		var nsView: Layout.View { get }
	}
	extension Layout.View: ViewConvertible {
		var nsView: Layout.View {
			return self
		}
	}
#else
	import UIKit
	
	protocol ViewConvertible {
		var uiView: Layout.View { get }
	}
	extension Layout.View: ViewConvertible {
		var uiView: Layout.View {
			return self
		}
	}
#endif

/// When a layout is applied, it can animate one of three ways:
///
/// - none: do not animate to the new layout
/// - all: animate to the new layout
/// - subsequent: animate to the new layout only if there was a previous layout
enum AnimationChoice {
	case none
	case all
	case subsequent
}


// This type handles a combination of `layoutMargin` and `safeAreaMargin` inset edges. If a `safeArea` edge is specified, it will be used instead of `layout` edge.
struct MarginEdges: OptionSet {
	static var none: MarginEdges { return MarginEdges(rawValue: 0) }
	static var topLayout: MarginEdges { return MarginEdges(rawValue: 1) }
	static var leadingLayout: MarginEdges { return MarginEdges(rawValue: 2) }
	static var bottomLayout: MarginEdges { return MarginEdges(rawValue: 4) }
	static var trailingLayout: MarginEdges { return MarginEdges(rawValue: 8) }
	static var topSafeArea: MarginEdges { return MarginEdges(rawValue: 16) }
	static var leadingSafeArea: MarginEdges { return MarginEdges(rawValue: 32) }
	static var bottomSafeArea: MarginEdges { return MarginEdges(rawValue: 64) }
	static var trailingSafeArea: MarginEdges { return MarginEdges(rawValue: 128) }
	static var allLayout: MarginEdges { return [.topLayout, .leadingLayout, .bottomLayout, .trailingLayout] }
	static var allSafeArea: MarginEdges { return [.topSafeArea, .leadingSafeArea, .bottomSafeArea, .trailingSafeArea] }
	let rawValue: UInt
	init(rawValue: UInt) {
		self.rawValue = rawValue
	}
}

#if os(macOS)
	extension NSView {
		/// Adds the views contained by `layout` in the arrangment described by the layout to `self`.
		///
		/// - Parameter layout: a set of views and layout descriptions
		func applyLayout(_ layout: Layout?) {
			applyLayoutToView(view: self, params: layout.map { (layout: $0, bounds: Layout.Bounds(view: self, marginEdges: .none)) })
		}
	}
#else
	extension UIView {
		/// Adds the views contained by `layout` in the arrangment described by the layout to `self`.
		///
		/// - Parameter layout: a set of views and layout descriptions
		func applyLayout(_ layout: Layout?) {
			applyLayoutToView(view: self, params: layout.map { (layout: $0, bounds: Layout.Bounds(view: self, marginEdges: $0.marginEdges)) })
		}
	}
	
	extension UIScrollView {
		/// Adds the views contained by `layout` in the arrangment described by the layout to `self`.
		///
		/// - Parameter layout: a set of views and layout descriptions
		@available(iOS 11, *)
		func applyContentLayout(_ layout: Layout?) {
			applyLayoutToView(view: self, params: layout.map { (layout: $0, bounds: Layout.Bounds(scrollView: self)) })
		}
	}
	
	extension UIViewController {
		/// Adds the views contained by `layout` in the arrangment described by the layout to `self`.
		///
		/// NOTE: prior to iOS 11, this is required to handle the UIViewController topLayoutGuide and bottomLayoutGuide.
		///
		/// - Parameter layout: a set of views and layout descriptions
		@available(iOS, introduced: 7.0, deprecated: 11.0, message: "This function exists to layout relative to view controller layout guides prior to iOS 11. On iOS 11 and later, use safe area margins and apply the layout directly to the view")
		func applyLayout(_ layout: Layout?) {
			if let l = layout, l.marginEdges.contains(.topSafeArea) || l.marginEdges.contains(.bottomSafeArea) {
				let wrapper = Layout(
					axis: .vertical,
					align: .fill,
					entities: [Layout.Entity(.layout(l, size: nil))]
				)
				applyLayoutToView(view: self.view, params: (layout: wrapper, bounds: Layout.Bounds(viewController: self, marginEdges: l.marginEdges)))
			} else {
				applyLayoutToView(view: self.view, params: layout.map { (layout: $0, bounds: Layout.Bounds(viewController: self, marginEdges: $0.marginEdges)) })
			}
		}
	}
#endif

/// A data structure for describing a layout as a series of nested columns and rows.
struct Layout {
	/// A rough equivalent to UIStackViewAlignment, minus baseline cases which aren't handled
	enum Alignment { case leading, trailing, center, fill }
	
	#if os(macOS)
		typealias Axis = NSUserInterfaceLayoutOrientation
		typealias View = NSView
		typealias Guide = NSLayoutGuide
	#else
		typealias Axis = UILayoutConstraintAxis
		typealias View = UIView
		typealias Guide = UILayoutGuide
	#endif
	
	/// Layout is either horizontal or vertical (although any element within the layout may be a layout in the perpendicular direction)
	let axis: Axis
	
	/// Within the horizontal row or vertical column, layout entities may fill, center or align-leading or align-trailing
	let align: Alignment
	
	/// The layout may extend to the view bounds or may be limited by the safeAreaMargins or layoutMargins. The safeArea insets supercede the layoutMargins (prior to iOS 11, safeArea is interpreted as UIViewController top/bottom layout guides when laying out within a UIViewController, otherwise it is treated as a synonym for the layoutMargins). This value has no effect on macOS.	
	let marginEdges: MarginEdges
	
	/// When applied to the top level `Layout` passed to 'applyLayout`, then replacing an existing layout on a view, if this variable is true, after applying the new layout, `layoutIfNeeded` will be called inside a `UIView.beginAnimations`/`UIView.endAnimations` block. Has no effect when set on a child `Layout`.
	let animate: AnimationChoice
	
	/// This is the list of views, spaces and sublayouts that will be layed out.
	var entities: [Entity]
	
	/// The default constructor assigns all values. In general, it's easier to use the `.horizontal` or `.vertical` constructor where possible.
	init(axis: Axis, align: Alignment = .fill, marginEdges: MarginEdges = .allSafeArea, animate: AnimationChoice = .subsequent, entities: [Entity]) {
		self.axis = axis
		self.align = align
		self.entities = entities
		self.marginEdges = marginEdges
		self.animate = animate
	}
	
	/// A convenience constructor for a horizontal layout
	static func horizontal(align: Alignment = .fill, marginEdges: MarginEdges = .allSafeArea, animate: AnimationChoice = .subsequent, _ entities: Entity...) -> Layout {
		return Layout(axis: .horizontal, align: align, marginEdges: marginEdges, animate: animate, entities: entities)
	}
	
	/// A convenience constructor for a vertical layout
	static func vertical(align: Alignment = .fill, marginEdges: MarginEdges = .allSafeArea, animate: AnimationChoice = .subsequent, _ entities: Entity...) -> Layout {
		return Layout(axis: .vertical, align: align, marginEdges: marginEdges, animate: animate, entities: entities)
	}
	
	// Used for removing all views from their superviews
	fileprivate func forEachView(_ visit: (View) -> ()) {
		entities.forEach { $0.forEachView(visit) }
	}

	/// The `Layout` describes a series of these `Entity`s which may be a space, a view or a sublayout. There is also a special `matched` layout which allows a series of "same length" entities.
	///
	/// - interViewSpace: AppKit and UIKit use an 8 screen unit space as the "standard" space between adjacent views.
	/// - space: an arbitrary space between views
	/// - view: a view with optional width and height (if not specified, the view will use its "intrinsic" size or will fill the available layout space)
	/// - layout: a nested layout which may be parallel or perpedicular to its container and whose size may be specified (like view)
	/// - matched: a sequence of alternating "same size" and independent entities (you can use `.space(0)` if you don't want independent entities).
	struct Entity {
		enum Content {
			case space(Dimension)
			case sizedView(Layout.View, Size?)
			indirect case layout(Layout, size: Size?)
			indirect case matched(Entity, [(independent: Entity, same: Entity)], priority: Dimension.Priority)
		}
		let content: Content
		init(_ content: Content) {
			self.content = content
		}
		
		fileprivate func forEachView(_ visit: (Layout.View) -> ()) {
			switch content {
			case .sizedView(let v, _):
				#if os(macOS)
					visit(v.nsView)
				#else
					visit(v.uiView)
				#endif
			case .layout(let l, _): l.forEachView(visit)
			case .matched(let entity, let pairArray, _):
				entity.forEachView(visit)
				pairArray.forEach {
					$0.same.forEachView(visit)
					$0.independent.forEachView(visit)
				}
			default: break
			}
		}
		
		static func space(_ dimension: Dimension = .standardSpace) -> Entity {
			return Entity(.space(dimension))
		}
		
		static func view(length: Dimension? = nil, breadth: Dimension? = nil, relative: Bool = false, _ view: ViewConvertible) -> Entity {
			let size = Size(length: length, breadth: breadth, relative: relative)
			#if os(macOS)
				return Entity(.sizedView(view.nsView, size))
			#else
				return Entity(.sizedView(view.uiView, size))
			#endif
		}
		
		static func horizontal(align: Alignment = .fill, length: Dimension? = nil, breadth: Dimension? = nil, relative: Bool = false, _ entities: Entity...) -> Entity {
			let size = Size(length: length, breadth: breadth, relative: relative)
			return Entity(.layout(Layout(axis: .horizontal, align: align, marginEdges: .none, entities: entities), size: size))
		}
		
		static func vertical(align: Alignment = .fill, length: Dimension? = nil, breadth: Dimension? = nil, relative: Bool = false, _ entities: Entity...) -> Entity {
			let size = Size(length: length, breadth: breadth, relative: relative)
			return Entity(.layout(Layout(axis: .vertical, align: align, marginEdges: .none, entities: entities), size: size))
		}
		
		static func matchedPair(_ left: Entity, _ right: Entity, separator: Entity = .space(), priority: Dimension.Priority = .required) -> Entity {
			return Entity(.matched(left, [(independent: separator, same: right)], priority: priority))
		}
		
		static func matched(_ first: Entity, _ subsequent: [(independent: Entity, same: Entity)], priority: Dimension.Priority = .required) -> Entity {
			return Entity(.matched(first, subsequent, priority: priority))
		}
	}

	/// A `Size` is the combination of both length (size of a layout object in the direction of layout) or breadth (size of a layout object perpendicular to the layout direction). If the length includes a ratio, it is relative to the parent container but the breadth can be relative to the length, allowing for specifying an aspect ratio.
	struct Size {
		let length: Dimension?
		let breadth: Dimension?
		let relative: Bool
		
		init(length: Dimension? = nil, breadth: Dimension?, relative: Bool = false) {
			self.length = length
			self.breadth = breadth
			self.relative = relative
		}
	}

	/// When length (size of a layout object in the direction of layout) or breadth (size of a layout object perpendicular to the layout direction) is specified, it can be specified:
	///	* relative to the parent container (ratio)
	///	* in raw screen units (constant)
	/// The greater/less than and priority can also be specified.
	struct Dimension: ExpressibleByFloatLiteral, ExpressibleByIntegerLiteral {
		typealias FloatLiteralType = Double
		typealias IntegerLiteralType = Int
		
		#if os(macOS)
			typealias Relation = NSLayoutConstraint.Relation
			typealias Priority = NSLayoutConstraint.Priority
		#else
			typealias Relation = NSLayoutRelation
			typealias Priority = UILayoutPriority
		#endif
		
		let ratio: CGFloat
		let constant: CGFloat
		let relationship: Relation
		let priority: Dimension.Priority
		init(ratio: CGFloat = 0, constant: CGFloat = 0, relationship: Dimension.Relation = .equal, priority: Dimension.Priority = .required) {
			self.ratio = ratio
			self.constant = constant
			self.relationship = relationship
			self.priority = priority
		}
		
		init(floatLiteral value: Double) {
			self.init(constant: CGFloat(value))
		}
		
		init(integerLiteral value: Int) {
			self.init(constant: CGFloat(value))
		}
		
		static var standardSpace: Dimension = 8
		
		static func lessThanOrEqualTo(ratio: CGFloat = 0, constant: CGFloat = 0, priority: Dimension.Priority = .required) -> Dimension {
			return Dimension(ratio: ratio, constant: constant, relationship: .lessThanOrEqual, priority: priority)
		}
		
		static func greaterThanOrEqualTo(ratio: CGFloat = 0, constant: CGFloat = 0, priority: Dimension.Priority = .required) -> Dimension {
			return Dimension(ratio: ratio, constant: constant, relationship: .greaterThanOrEqual, priority: priority)
		}
		
		static func equalTo(ratio: CGFloat = 0, constant: CGFloat = 0, priority: Dimension.Priority = .required) -> Dimension {
			return Dimension(ratio: ratio, constant: constant, relationship: .equal, priority: priority)
		}
		
		static var fillRemaining: Dimension {
			return greaterThanOrEqualTo(constant: 0, priority: .userHigh)
		}
	}

	/// Bounds are used internally to capture a set of guides and anchors. On the Mac, these are merely copied from a single NSLayoutGuide or an NSView. On iOS, these may be copied from a blend of UIViewController top/bottomLayoutGuides, safeAreaLayoutGuides, layoutMarginsGuides or a UIView.
	fileprivate struct Bounds {
		var leading: NSLayoutXAxisAnchor
		var top: NSLayoutYAxisAnchor
		var trailing: NSLayoutXAxisAnchor
		var bottom: NSLayoutYAxisAnchor
		var width: NSLayoutDimension
		var height: NSLayoutDimension
		var centerX: NSLayoutXAxisAnchor
		var centerY: NSLayoutYAxisAnchor
		
		fileprivate init(box: Layout.Box) {
			leading = box.leadingAnchor
			top = box.topAnchor
			trailing = box.trailingAnchor
			bottom = box.bottomAnchor
			width = box.widthAnchor
			height = box.heightAnchor
			centerX = box.centerXAnchor
			centerY = box.centerYAnchor
		}
		
		#if os(iOS)
			@available(iOS 11, *)
			fileprivate init(scrollView: UIScrollView) {
				leading = scrollView.contentLayoutGuide.leadingAnchor
				top = scrollView.contentLayoutGuide.topAnchor
				trailing = scrollView.contentLayoutGuide.trailingAnchor
				bottom = scrollView.contentLayoutGuide.bottomAnchor
				width = scrollView.contentLayoutGuide.widthAnchor
				height = scrollView.contentLayoutGuide.heightAnchor
				centerX = scrollView.contentLayoutGuide.centerXAnchor
				centerY = scrollView.contentLayoutGuide.centerYAnchor
			}
			
			@available(iOS, introduced: 7.0, deprecated: 11.0)
			fileprivate init(viewController: UIViewController, marginEdges: MarginEdges) {
				let view = viewController.view!
				leading = marginEdges.contains(.leadingSafeArea) || marginEdges.contains(.leadingLayout) ? view.layoutMarginsGuide.leadingAnchor : view.leadingAnchor
				top = marginEdges.contains(.topSafeArea) ? viewController.topLayoutGuide.bottomAnchor : (marginEdges.contains(.topLayout) ? view.layoutMarginsGuide.topAnchor : view.topAnchor)
				trailing = marginEdges.contains(.trailingSafeArea) || marginEdges.contains(.trailingLayout) ? view.layoutMarginsGuide.trailingAnchor : view.trailingAnchor
				bottom = marginEdges.contains(.bottomSafeArea) ? viewController.bottomLayoutGuide.topAnchor : (marginEdges.contains(.bottomLayout) ? view.layoutMarginsGuide.bottomAnchor : view.bottomAnchor)
				width = (marginEdges.contains(.leadingSafeArea) || marginEdges.contains(.leadingLayout)) && (marginEdges.contains(.trailingSafeArea) || marginEdges.contains(.trailingLayout)) ? view.layoutMarginsGuide.widthAnchor : view.widthAnchor
				height = (marginEdges.contains(.topSafeArea) || marginEdges.contains(.topLayout)) && (marginEdges.contains(.bottomSafeArea) || marginEdges.contains(.bottomLayout)) ? view.layoutMarginsGuide.heightAnchor : view.heightAnchor
				centerX = (marginEdges.contains(.leadingSafeArea) || marginEdges.contains(.leadingLayout)) && (marginEdges.contains(.trailingSafeArea) || marginEdges.contains(.trailingLayout)) ? view.layoutMarginsGuide.centerXAnchor : view.centerXAnchor
				centerY = (marginEdges.contains(.topSafeArea) || marginEdges.contains(.topLayout)) && (marginEdges.contains(.bottomSafeArea) || marginEdges.contains(.bottomLayout)) ? view.layoutMarginsGuide.centerYAnchor : view.centerYAnchor
			}
			
			fileprivate init(view: Layout.View, marginEdges: MarginEdges) {
				if #available(iOS 11.0, *) {
					#if swift(>=4)
						leading = marginEdges.contains(.leadingSafeArea) ? view.safeAreaLayoutGuide.leadingAnchor : (marginEdges.contains(.leadingLayout) ? view.layoutMarginsGuide.leadingAnchor : view.leadingAnchor)
						top = marginEdges.contains(.topSafeArea) ? view.safeAreaLayoutGuide.topAnchor : (marginEdges.contains(.topLayout) ? view.layoutMarginsGuide.topAnchor : view.topAnchor)
						trailing = marginEdges.contains(.trailingSafeArea) ? view.safeAreaLayoutGuide.trailingAnchor : (marginEdges.contains(.trailingLayout) ? view.layoutMarginsGuide.trailingAnchor : view.trailingAnchor)
						bottom = marginEdges.contains(.bottomSafeArea) ? view.safeAreaLayoutGuide.bottomAnchor : (marginEdges.contains(.bottomLayout) ? view.layoutMarginsGuide.bottomAnchor : view.bottomAnchor)
						width = (marginEdges.contains(.leadingSafeArea) && marginEdges.contains(.trailingSafeArea)) ? view.safeAreaLayoutGuide.widthAnchor : (marginEdges.contains(.leadingLayout) && marginEdges.contains(.trailingLayout) ? view.layoutMarginsGuide.widthAnchor : view.widthAnchor)
						height = (marginEdges.contains(.leadingSafeArea) && marginEdges.contains(.trailingSafeArea)) ? view.safeAreaLayoutGuide.heightAnchor : (marginEdges.contains(.leadingLayout) && marginEdges.contains(.trailingLayout) ? view.layoutMarginsGuide.heightAnchor : view.heightAnchor)
						centerX = (marginEdges.contains(.leadingSafeArea) && marginEdges.contains(.trailingSafeArea)) ? view.safeAreaLayoutGuide.centerXAnchor : (marginEdges.contains(.leadingLayout) && marginEdges.contains(.trailingLayout) ? view.layoutMarginsGuide.centerXAnchor : view.centerXAnchor)
						centerY = (marginEdges.contains(.leadingSafeArea) && marginEdges.contains(.trailingSafeArea)) ? view.safeAreaLayoutGuide.centerYAnchor : (marginEdges.contains(.leadingLayout) && marginEdges.contains(.trailingLayout) ? view.layoutMarginsGuide.centerYAnchor : view.centerYAnchor)
					#else
						leading = marginEdges.contains(.leadingSafeArea) || marginEdges.contains(.leadingLayout) ? view.layoutMarginsGuide.leadingAnchor : view.leadingAnchor
						top = marginEdges.contains(.topSafeArea) || marginEdges.contains(.topLayout) ? view.layoutMarginsGuide.topAnchor : view.topAnchor
						trailing = marginEdges.contains(.trailingSafeArea) || marginEdges.contains(.trailingLayout) ? view.layoutMarginsGuide.trailingAnchor : view.trailingAnchor
						bottom = marginEdges.contains(.bottomSafeArea) || marginEdges.contains(.bottomLayout) ? view.layoutMarginsGuide.bottomAnchor : view.bottomAnchor
						width = (marginEdges.contains(.leadingSafeArea) || marginEdges.contains(.leadingLayout)) && (marginEdges.contains(.trailingSafeArea) || marginEdges.contains(.trailingLayout)) ? view.layoutMarginsGuide.widthAnchor : view.widthAnchor
						height = (marginEdges.contains(.topSafeArea) || marginEdges.contains(.topLayout)) && (marginEdges.contains(.bottomSafeArea) || marginEdges.contains(.bottomLayout)) ? view.layoutMarginsGuide.heightAnchor : view.heightAnchor
						centerX = (marginEdges.contains(.leadingSafeArea) || marginEdges.contains(.leadingLayout)) && (marginEdges.contains(.trailingSafeArea) || marginEdges.contains(.trailingLayout)) ? view.layoutMarginsGuide.centerXAnchor : view.centerXAnchor
						centerY = (marginEdges.contains(.topSafeArea) || marginEdges.contains(.topLayout)) && (marginEdges.contains(.bottomSafeArea) || marginEdges.contains(.bottomLayout)) ? view.layoutMarginsGuide.centerYAnchor : view.centerYAnchor
					#endif
				} else {
					leading = marginEdges.contains(.leadingSafeArea) || marginEdges.contains(.leadingLayout) ? view.layoutMarginsGuide.leadingAnchor : view.leadingAnchor
					top = marginEdges.contains(.topSafeArea) || marginEdges.contains(.topLayout) ? view.layoutMarginsGuide.topAnchor : view.topAnchor
					trailing = marginEdges.contains(.trailingSafeArea) || marginEdges.contains(.trailingLayout) ? view.layoutMarginsGuide.trailingAnchor : view.trailingAnchor
					bottom = marginEdges.contains(.bottomSafeArea) || marginEdges.contains(.bottomLayout) ? view.layoutMarginsGuide.bottomAnchor : view.bottomAnchor
					width = (marginEdges.contains(.leadingSafeArea) || marginEdges.contains(.leadingLayout)) && (marginEdges.contains(.trailingSafeArea) || marginEdges.contains(.trailingLayout)) ? view.layoutMarginsGuide.widthAnchor : view.widthAnchor
					height = (marginEdges.contains(.topSafeArea) || marginEdges.contains(.topLayout)) && (marginEdges.contains(.bottomSafeArea) || marginEdges.contains(.bottomLayout)) ? view.layoutMarginsGuide.heightAnchor : view.heightAnchor
					centerX = (marginEdges.contains(.leadingSafeArea) || marginEdges.contains(.leadingLayout)) && (marginEdges.contains(.trailingSafeArea) || marginEdges.contains(.trailingLayout)) ? view.layoutMarginsGuide.centerXAnchor : view.centerXAnchor
					centerY = (marginEdges.contains(.topSafeArea) || marginEdges.contains(.topLayout)) && (marginEdges.contains(.bottomSafeArea) || marginEdges.contains(.bottomLayout)) ? view.layoutMarginsGuide.centerYAnchor : view.centerYAnchor
				}
			}
		#else
			fileprivate init(view: Layout.View, marginEdges: MarginEdges) {
				leading = view.leadingAnchor
				top = view.topAnchor
				trailing = view.trailingAnchor
				bottom = view.bottomAnchor
				width = view.widthAnchor
				height = view.heightAnchor
				centerX = view.centerXAnchor
				centerY = view.centerYAnchor
			}
		#endif
	}

	fileprivate struct State {
		let view: View
		let storage: Storage
		
		var dimension: Dimension? = nil
		var previousEntityBounds: Bounds? = nil
		var containerBounds: Bounds
		
		init(containerBounds: Bounds, in view: View, storage: Storage) {
			self.containerBounds = containerBounds
			self.view = view
			self.storage = storage
		}
	}

	fileprivate class Storage: NSObject {
		let layout: Layout
		var constraints: [NSLayoutConstraint] = []
		var boxes: [Layout.Box] = []
		
		init(layout: Layout) {
			self.layout = layout
		}
	}

	#if true && DEBUG
		fileprivate typealias Box = Layout.View
	#else
		fileprivate typealias Box = Layout.Guide
	#endif
	
	fileprivate func constrain(bounds: Bounds, leading: Dimension, length: Dimension?, breadth: Dimension?, relative: Bool, state: inout State) {
		if axis == .horizontal {
			let leadingAnchor = bounds.leading
			let preceedingAnchor = state.containerBounds.leading
			let leadingConstraint: NSLayoutConstraint
			switch leading.relationship {
			case .equal: leadingConstraint = leadingAnchor.constraint(equalTo: preceedingAnchor, constant: leading.constant)
			case .lessThanOrEqual: leadingConstraint = leadingAnchor.constraint(lessThanOrEqualTo: preceedingAnchor, constant: leading.constant)
			case .greaterThanOrEqual: leadingConstraint = leadingAnchor.constraint(greaterThanOrEqualTo: preceedingAnchor, constant: leading.constant)
			}
			leadingConstraint.priority = leading.priority
			state.storage.constraints.append(leadingConstraint)
			leadingConstraint.isActive = true
			
			if let l = length {
				let widthAnchor = bounds.width
				let widthConstraint: NSLayoutConstraint
				switch l.relationship {
				case .equal: widthConstraint = widthAnchor.constraint(equalTo: state.containerBounds.width, multiplier: l.ratio, constant: l.constant)
				case .lessThanOrEqual: widthConstraint = widthAnchor.constraint(lessThanOrEqualTo: state.containerBounds.width, multiplier: l.ratio, constant: l.constant)
				case .greaterThanOrEqual: widthConstraint = widthAnchor.constraint(greaterThanOrEqualTo: state.containerBounds.width, multiplier: l.ratio, constant: l.constant)
				}
				widthConstraint.priority = l.priority
				state.storage.constraints.append(widthConstraint)
				widthConstraint.isActive = true
			}
			
			if let b = breadth {
				let heightAnchor = bounds.height
				let secondAnchor = relative ? bounds.width : state.containerBounds.height
				let heightConstraint: NSLayoutConstraint
				switch b.relationship {
				case .equal: heightConstraint = heightAnchor.constraint(equalTo: secondAnchor, multiplier: b.ratio, constant: b.constant)
				case .lessThanOrEqual: heightConstraint = heightAnchor.constraint(lessThanOrEqualTo: secondAnchor, multiplier: b.ratio, constant: b.constant)
				case .greaterThanOrEqual: heightConstraint = heightAnchor.constraint(greaterThanOrEqualTo: secondAnchor, multiplier: b.ratio, constant: b.constant)
				}
				heightConstraint.priority = b.priority
				state.storage.constraints.append(heightConstraint)
				heightConstraint.isActive = true
			}
			
			switch self.align {
			case .leading:
				let top = bounds.top.constraint(equalTo: state.containerBounds.top)
				let bottom = bounds.bottom.constraint(equalTo: state.containerBounds.bottom)
				let bottom2 = bounds.bottom.constraint(lessThanOrEqualTo: state.containerBounds.bottom)
				top.priority = .required
				bottom.priority = .userLow
				bottom2.priority = .userHigh
				top.isActive = true
				bottom.isActive = true
				bottom2.isActive = true
				state.storage.constraints.append(top)
				state.storage.constraints.append(bottom)
				state.storage.constraints.append(bottom2)
			case .trailing:
				let top = bounds.top.constraint(equalTo: state.containerBounds.top)
				let top2 = bounds.top.constraint(greaterThanOrEqualTo: state.containerBounds.top)
				let bottom = bounds.bottom.constraint(equalTo: state.containerBounds.bottom)
				top.priority = .userLow
				top2.priority = .userHigh
				bottom.priority = .required
				top.isActive = true
				top2.isActive = true
				bottom.isActive = true
				state.storage.constraints.append(top)
				state.storage.constraints.append(top2)
				state.storage.constraints.append(bottom)
			case .center:
				let center = bounds.centerY.constraint(equalTo: state.containerBounds.centerY)
				center.priority = .required
				center.isActive = true
				state.storage.constraints.append(center)
				let height = bounds.height.constraint(equalTo: state.containerBounds.height)
				height.priority = .userLow
				height.isActive = true
				state.storage.constraints.append(height)
			case .fill:
				let top = bounds.top.constraint(equalTo: state.containerBounds.top)
				let bottom = bounds.bottom.constraint(equalTo: state.containerBounds.bottom)
				top.priority = .userHigh
				bottom.priority = .userHigh
				top.isActive = true
				bottom.isActive = true
				state.storage.constraints.append(top)
				state.storage.constraints.append(bottom)
			}
			
			state.containerBounds.leading = bounds.trailing
		} else {
			let leadingAnchor = bounds.top
			let preceedingAnchor = state.containerBounds.top
			let leadingConstraint: NSLayoutConstraint
			switch leading.relationship {
			case .equal: leadingConstraint = leadingAnchor.constraint(equalTo: preceedingAnchor, constant: leading.constant)
			case .lessThanOrEqual: leadingConstraint = leadingAnchor.constraint(lessThanOrEqualTo: preceedingAnchor, constant: leading.constant)
			case .greaterThanOrEqual: leadingConstraint = leadingAnchor.constraint(greaterThanOrEqualTo: preceedingAnchor, constant: leading.constant)
			}
			leadingConstraint.priority = leading.priority
			state.storage.constraints.append(leadingConstraint)
			leadingConstraint.isActive = true
			
			if let l = length {
				let heightAnchor = bounds.height
				let heightConstraint: NSLayoutConstraint
				switch l.relationship {
				case .equal: heightConstraint = heightAnchor.constraint(equalTo: state.containerBounds.height, multiplier: l.ratio, constant: l.constant)
				case .lessThanOrEqual: heightConstraint = heightAnchor.constraint(lessThanOrEqualTo: state.containerBounds.height, multiplier: l.ratio, constant: l.constant)
				case .greaterThanOrEqual: heightConstraint = heightAnchor.constraint(greaterThanOrEqualTo: state.containerBounds.height, multiplier: l.ratio, constant: l.constant)
				}
				heightConstraint.priority = l.priority
				state.storage.constraints.append(heightConstraint)
				heightConstraint.isActive = true
			}
			
			if let b = breadth {
				let widthAnchor = bounds.width
				let secondAnchor = relative ? bounds.height : state.containerBounds.width
				let widthConstraint: NSLayoutConstraint
				switch b.relationship {
				case .equal: widthConstraint = widthAnchor.constraint(equalTo: secondAnchor, multiplier: b.ratio, constant: b.constant)
				case .lessThanOrEqual: widthConstraint = widthAnchor.constraint(lessThanOrEqualTo: secondAnchor, multiplier: b.ratio, constant: b.constant)
				case .greaterThanOrEqual: widthConstraint = widthAnchor.constraint(greaterThanOrEqualTo: secondAnchor, multiplier: b.ratio, constant: b.constant)
				}
				widthConstraint.priority = b.priority
				state.storage.constraints.append(widthConstraint)
				widthConstraint.isActive = true
			}
			
			switch self.align {
			case .leading:
				let leading = bounds.leading.constraint(equalTo: state.containerBounds.leading)
				let trailing = bounds.trailing.constraint(equalTo: state.containerBounds.trailing)
				let trailing2 = bounds.trailing.constraint(lessThanOrEqualTo: state.containerBounds.trailing)
				leading.priority = .required
				trailing.priority = .userLow
				trailing2.priority = .userHigh
				leading.isActive = true
				trailing.isActive = true
				trailing2.isActive = true
				state.storage.constraints.append(leading)
				state.storage.constraints.append(trailing)
				state.storage.constraints.append(trailing2)
			case .trailing:
				let leading = bounds.leading.constraint(equalTo: state.containerBounds.leading)
				let leading2 = bounds.leading.constraint(greaterThanOrEqualTo: state.containerBounds.leading)
				let trailing = bounds.trailing.constraint(equalTo: state.containerBounds.trailing)
				leading.priority = .userLow
				leading2.priority = .userHigh
				trailing.priority = .required
				leading.isActive = true
				leading2.isActive = true
				trailing.isActive = true
				state.storage.constraints.append(leading)
				state.storage.constraints.append(leading2)
				state.storage.constraints.append(trailing)
			case .center:
				let center = bounds.centerX.constraint(equalTo: state.containerBounds.centerX)
				center.priority = .required
				center.isActive = true
				state.storage.constraints.append(center)
				let width = bounds.width.constraint(equalTo: state.containerBounds.width)
				width.priority = .userLow
				width.isActive = true
				state.storage.constraints.append(width)
			case .fill:
				let leading = bounds.leading.constraint(equalTo: state.containerBounds.leading)
				let trailing = bounds.trailing.constraint(equalTo: state.containerBounds.trailing)
				leading.priority = .userHigh
				trailing.priority = .userHigh
				leading.isActive = true
				trailing.isActive = true
				state.storage.constraints.append(leading)
				state.storage.constraints.append(trailing)
			}
			
			state.containerBounds.top = bounds.bottom
		}
	}
	
	@discardableResult
	fileprivate func layout(entity: Entity, state: inout State, needDimensionAnchor: Bool = false) -> NSLayoutDimension? {
		switch entity.content {
		case .space(let dimension):
			if let d = state.dimension, (d.ratio != 0 || d.constant != 0) {
				let box = Layout.Box()
				state.view.addLayoutBox(box)
				state.storage.boxes.append(box)
				constrain(bounds: Bounds(box: box), leading: Dimension(), length: d, breadth: nil, relative: false, state: &state)
				state.previousEntityBounds = nil
			}
			if dimension.ratio != 0 || needDimensionAnchor {
				let box = Layout.Box()
				state.view.addLayoutBox(box)
				state.storage.boxes.append(box)
				constrain(bounds: Bounds(box: box), leading: Dimension(), length: dimension, breadth: nil, relative: false, state: &state)
				return axis == .horizontal ? box.widthAnchor : box.heightAnchor
			}
			state.dimension = dimension
			return nil
		case .layout(let l, let size):
			let box = Layout.Box()
			state.view.addLayoutBox(box)
			state.storage.boxes.append(box)
			let bounds = Bounds(box: box)
			l.add(to: state.view, containerBounds: bounds, storage: state.storage)
			constrain(bounds: bounds, leading: state.dimension ?? Dimension(), length: size?.length, breadth: size?.breadth, relative: size?.relative ?? false, state: &state)
			state.dimension = nil
			state.previousEntityBounds = bounds
			return needDimensionAnchor ? (axis == .horizontal ? box.widthAnchor : box.heightAnchor) : nil
		case .matched(let first, let pairs, let priority):
			if needDimensionAnchor {
				let box = Layout.Box()
				state.view.addLayoutBox(box)
				state.storage.boxes.append(box)
				var subState = State(containerBounds: state.containerBounds, in: state.view, storage: state.storage)
				layout(entity: entity, state: &subState)
				state.dimension = nil
				state.previousEntityBounds = Bounds(box: box)
				return axis == .horizontal ? box.widthAnchor : box.heightAnchor
			} else {
				let first = layout(entity: first, state: &state, needDimensionAnchor: true)!
				for p in pairs {
					layout(entity: p.independent, state: &state)
					let match = layout(entity: p.same, state: &state, needDimensionAnchor: true)!
					let constraint = match.constraint(equalTo: first)
					state.storage.constraints.append(constraint)
					constraint.priority = priority
					constraint.isActive = true
				}
				return nil
			}
		case .sizedView(let v, let size):
			#if os(macOS)
				let view = v.nsView
			#else
				let view = v.uiView
			#endif
			view.translatesAutoresizingMaskIntoConstraints = false
			state.view.addSubview(view)
			constrain(bounds: Bounds(view: view, marginEdges: .none), leading: state.dimension ?? Dimension(), length: size?.length, breadth: size?.breadth, relative: size?.relative ?? false, state: &state)
			state.dimension = nil
			state.previousEntityBounds = Bounds(view: view, marginEdges: .none)
			return needDimensionAnchor ? (axis == .horizontal ? view.widthAnchor : view.heightAnchor) : nil
		}
	}
	
	fileprivate func add(to view: Layout.View, containerBounds: Bounds, storage: Storage) {
		var state = State(containerBounds: containerBounds, in: view, storage: storage)
		for entity in entities {
			layout(entity: entity, state: &state)
		}
		if let previous = state.previousEntityBounds {
			switch axis {
			case .horizontal:
				let trailingAnchor = previous.trailing
				let boundsTrailingAnchor = state.containerBounds.trailing
				let trailingConstraint: NSLayoutConstraint
				let trailing = state.dimension ?? Dimension()
				
				// NOTE: we must invert the relationship since we're laying out backwards
				switch trailing.relationship {
				case .equal: trailingConstraint = trailingAnchor.constraint(equalTo: boundsTrailingAnchor, constant: -trailing.constant)
				case .lessThanOrEqual: trailingConstraint = trailingAnchor.constraint(greaterThanOrEqualTo: boundsTrailingAnchor, constant: -trailing.constant)
				case .greaterThanOrEqual: trailingConstraint = trailingAnchor.constraint(lessThanOrEqualTo: boundsTrailingAnchor, constant: -trailing.constant)
				}
				
				trailingConstraint.priority = trailing.priority
				state.storage.constraints.append(trailingConstraint)
				trailingConstraint.isActive = true
			case .vertical:
				let trailingAnchor = previous.bottom
				let boundsTrailingAnchor = state.containerBounds.bottom
				let trailingConstraint: NSLayoutConstraint
				let trailing = state.dimension ?? Dimension()
				
				// NOTE: we must invert the relationship since we're laying out backwards
				switch trailing.relationship {
				case .equal: trailingConstraint = trailingAnchor.constraint(equalTo: boundsTrailingAnchor, constant: -trailing.constant)
				case .lessThanOrEqual: trailingConstraint = trailingAnchor.constraint(greaterThanOrEqualTo: boundsTrailingAnchor, constant: -trailing.constant)
				case .greaterThanOrEqual: trailingConstraint = trailingAnchor.constraint(lessThanOrEqualTo: boundsTrailingAnchor, constant: -trailing.constant)
				}
				
				trailingConstraint.priority = trailing.priority
				state.storage.constraints.append(trailingConstraint)
				trailingConstraint.isActive = true
			}
		}
	}
}

// NOTE:
//
// Views often have their own intrinsic size, and they maintain this size at
// either the `.defaultLow` or `.defaultHigh` priority. Unfortunately, layout
// doesn't work well if this intrinsic priority is perfectly balanced with the
// user-applied layout priority.
//
// For this reason, CwlLayout defaults to using the following layout priorities
// which are scaled to be slightly different to the default priorities. This
// allows you to easily set layout priorities above, between or below the
// intrinisic priorities without always resorting to `.required`.
//
extension Layout.Dimension.Priority {
	#if os(macOS)
		#if swift(>=4)
			static let userLow = NSLayoutConstraint.Priority(rawValue: NSLayoutConstraint.Priority.defaultLow.rawValue * 0.875)
			static let userMid = NSLayoutConstraint.Priority(rawValue: NSLayoutConstraint.Priority.required.rawValue * 0.5)
			static let userHigh = NSLayoutConstraint.Priority(rawValue: NSLayoutConstraint.Priority.defaultHigh.rawValue * 1.125)
		#else
			static let userLow = NSLayoutPriorityDefaultLow * 0.875
			static let userMid = NSLayoutPriorityRequired * 0.5
			static let userHigh = NSLayoutPriorityDefaultHigh * 1.125
		#endif
	#else
		#if swift(>=4)
			static let userLow = UILayoutPriority(rawValue: UILayoutPriority.defaultLow.rawValue * 0.875)
			static let userMid = UILayoutPriority(rawValue: UILayoutPriority.required.rawValue * 0.5)
			static let userHigh = UILayoutPriority(rawValue: UILayoutPriority.defaultHigh.rawValue * 1.125)
		#else
			static let userLow = UILayoutPriorityDefaultLow * 0.875
			static let userMid = UILayoutPriorityRequired * 0.5
			static let userHigh = UILayoutPriorityDefaultHigh * 1.125
		#endif
	#endif
}

extension Layout.View {
	// DEBUGGING TIP:
	// As of Xcode 8, the "Debug View Hierarchy" option does not show layout guides, making debugging of constraints involving layout guides tricky. To aid debugging in these cases, set the following condition to `true && DEBUG` and CwlLayout will create views instead of layout guides.
	// Otherwise, you can set this to `false && DEBUG`.
	#if true && DEBUG
		fileprivate func addLayoutBox(_ layoutBox: Layout.Box) {
			layoutBox.translatesAutoresizingMaskIntoConstraints = false
			self.addSubview(layoutBox)
		}
		fileprivate func removeLayoutBox(_ layoutBox: Layout.Box) {
			layoutBox.removeFromSuperview()
		}
	#else
		fileprivate func addLayoutBox(_ layoutBox: Layout.Box) {
			self.addLayoutGuide(layoutBox)
		}
		fileprivate func removeLayoutBox(_ layoutBox: Layout.Box) {
			self.removeLayoutGuide(layoutBox)
		}
	#endif
}

fileprivate var associatedLayoutKey = NSObject()
fileprivate func setLayout(_ newValue: Layout.Storage?, for object: Layout.View) {
	objc_setAssociatedObject(object, &associatedLayoutKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
}
fileprivate func getLayout(for view: Layout.View) -> Layout.Storage? {
	return objc_getAssociatedObject(view, &associatedLayoutKey) as? Layout.Storage
}

fileprivate extension Layout.View {
	func remove(constraintsAndBoxes previousLayout: Layout.Storage?, subviews: Set<Layout.View>) {
		guard let previous = previousLayout else { return }
		for constraint in previous.constraints {
			constraint.isActive = false
		}
		for box in previous.boxes {
			self.removeLayoutBox(box)
		}
		subviews.forEach { $0.removeFromSuperview() }
	}
}

fileprivate func applyLayoutToView(view: Layout.View, params: (layout: Layout, bounds: Layout.Bounds)?) {
	var removedViews = Set<Layout.View>()
	
	// Check for a previous layout and get the old views
	let previous = getLayout(for: view)
	previous?.layout.forEachView { view in removedViews.insert(view) }
	
	guard let (layout, bounds) = params else {
		// If there's no new layout, remove the old layout and we're done
		view.remove(constraintsAndBoxes: previous, subviews: removedViews)
		return
	}
	
	// Check if this will be animated
	let shouldAnimate = layout.animate != .none && (previous != nil || layout.animate != .subsequent)
	
	// Exclude views in the new layout from the removed set. If we're animating, we'll need animated and added sets too.
	var animatedViews = Set<Layout.View>()
	var addedViews = Set<Layout.View>()
	layout.forEachView { v in
		if let animated = removedViews.remove(v), shouldAnimate {
			animatedViews.insert(animated)
		} else if shouldAnimate {
			addedViews.insert(v)
		}
	}
	
	#if os(macOS)
		view.remove(constraintsAndBoxes: previous, subviews: removedViews)
		let storage = Layout.Storage(layout: layout)
		layout.add(to: view, containerBounds: bounds, storage: storage)
		setLayout(storage, for: view)
		return
	#else
		// Now that we know the precise removed set, remove them.
		if shouldAnimate && addedViews.count == 0 && removedViews.count > 0 {
			// If we're animating the removal of views but not the insertion of views, animate this removal
			UIView.transition(with: view, duration: 0.2, options: [.transitionCrossDissolve, .allowUserInteraction], animations: {
				view.remove(constraintsAndBoxes: previous, subviews: removedViews)
			}, completion: { completed in })
		} else {
			view.remove(constraintsAndBoxes: previous, subviews: removedViews)
		}
		
		// Apply the new layout
		let storage = Layout.Storage(layout: layout)
		layout.add(to: view, containerBounds: bounds, storage: storage)
		
		// If we're not animating, store the layout and we're done.
		if !shouldAnimate {
			setLayout(storage, for: view)
			return
		}
		
		if addedViews.count > 0 {
			// Apply the layout, so new views have a precise size
			view.layoutIfNeeded()
			
			// Remove the new views and revert to the old layout
			view.remove(constraintsAndBoxes: storage, subviews: addedViews)
			if let p = previous {
				let oldStorage = Layout.Storage(layout: layout)
				p.layout.add(to: view, containerBounds: bounds, storage: oldStorage)

				// Immediately remove the old constraints but keep the old views
				view.remove(constraintsAndBoxes: oldStorage, subviews: [])
			}
			
			// Animate the simultaneous removal and addition of new views
			UIView.transition(with: view, duration: 0.2, options: [.transitionCrossDissolve, .allowUserInteraction], animations: {
				removedViews.forEach { $0.removeFromSuperview() }
				addedViews.forEach { view.addSubview($0) }
			}, completion: { completed in })
			
			// Reapply the new layout. Since the new views are already in-place
			let reapplyStorage = Layout.Storage(layout: layout)
			layout.add(to: view, containerBounds: bounds, storage: reapplyStorage)
			setLayout(reapplyStorage, for: view)
		} else {
			setLayout(storage, for: view)
		}
		
		// Animate the frames of the new layout
		UIView.animate(withDuration: 0.2, delay: 0, options: [.allowUserInteraction], animations: {
			view.layoutIfNeeded()
		}, completion: { completed in })
	#endif
}
